import { Select, Table } from 'antd'
import { nanoid } from 'nanoid'
import { forwardRef, useEffect, useRef, useState } from 'react'

import { add } from '@/utils/calculate'
import pinyinUtil from '@/utils/pinyinUtil'
import { requestWithTry } from '@/utils/request'

import { helpColumnsRender } from '../AekTable/utils'

/**
 * @typedef {Object} OtherProps
 * @property {Promise} [api] 接口
 * @property {Object} [params] 额外参数
 * @property {boolean} [loadAll] 是否一次性加载后端全部数据
 * @property {function} [contentRender] 处理返回值
 * @property {boolean} [selFirst] 初次加载是否默认选中第一项
 * @property {string | number | Object | Array<string | number | Object>} [value] 当labelInValue为true时，返回的是完整对象
 * @property {boolean} [labelInValue] 重写了antd的传参格式：1、当labelInValue为true时，外部value传入都改为{[fieldNamesValue]: any, [fieldNamesLabel]: any, ...other}的形式；2、当下拉数据中对应value的项被删除时，使用该属性，外部赋值的时候带上fieldNamesLabel对应的字段，即可正确展示选择框内的值
 * @property {import("antd/es/table").TableProps['columns'] & Array<import("@/components/AekTable/utils").ColumnsProps>} [columns] 表格项配置
 */

/**
 * @typedef {import("antd/es/select").SelectProps & OtherProps} AekSelectProps
 */

/**
 * @param {AekSelectProps} props
 * @returns
 */
const AekSelect = (props, ref) => {
  const {
    // 以下为自定义属性
    api,
    params = {},
    loadAll = true,
    contentRender = _ => _,
    selFirst = false,
    fieldNames = {},
    // 以下为antd属性
    labelInValue = false,
    columns = [],
    mode = undefined,
    onSearch,
    value,
    onChange,
    onClick,
    onBlur,
    ...otherProps
  } = props

  const { value: fieldNamesValue = 'id', label: fieldNamesLabel = 'name', ...fieldNamesOther } = fieldNames

  const [open, setOpen] = useState(false)

  const [dataSource, setDataSource] = useState([])
  const [loading, setLoading] = useState(false)
  const [searchText, setSearchText] = useState()
  const popupRenderId = useRef(nanoid())

  helpColumnsRender(columns)

  /**
   * 请求数据
   * @param {string} keywords
   * @returns
   */
  const request = async keywords => {
    if (!api) return null
    setLoading(true)
    const { data } = await requestWithTry(api, { keywords, ...params, pageSize: loadAll ? 9999 : 10 }).finally(() => {
      setLoading(false)
    })
    const dataSource = contentRender(data.content || data || [])
    // 返回值为对象时，拿到全部数据后触发一次onChange，
    // 原因：因为有其他地方用到了这个对象里的其他属性，而当编辑的时候，后端有可能未返回其他属性
    if (labelInValue && value !== undefined && value !== null) {
      const record = dataSource.find(item => item[fieldNamesValue] === value[fieldNamesValue])
      if (record) {
        onChange?.(record)
      }
    }
    // 初次加载默认选中第一项
    if (!value && selFirst && dataSource.length > 0) {
      onChange?.(labelInValue ? dataSource[0] : dataSource[0][fieldNamesValue])
    }
    setDataSource(dataSource)
  }

  useEffect(() => {
    const getData = setTimeout(() => {
      if (!searchText) return
      request(searchText)
    }, 400)
    return () => clearTimeout(getData)
  }, [searchText])

  useEffect(() => {
    request()
  }, [JSON.stringify(params)])

  /**
   * 搜索
   * @param {string} text
   */
  const triggerSearch = text => {
    if (!loadAll) {
      setSearchText(text)
    }
    onSearch && onSearch(text)
  }

  /**
   * 获取value的实际值，传给antd
   * 1. 如果labelInValue，则返回{label, value}
   * @returns {string | number | Array<string | number> | { label: string, value: string | number } | Array<{ label: string, value: string | number }> | undefined}
   */
  const getValueForAntd = () => {
    if (labelInValue) {
      if (mode === 'multiple') {
        return value?.map(item => ({ label: item[fieldNamesLabel], value: item[fieldNamesValue] }))
      } else {
        return value ? { label: value[fieldNamesLabel], value: value[fieldNamesValue] } : undefined
      }
    } else {
      return value
    }
  }

  const valueForAntd = getValueForAntd()

  const triggerChange = (value, option) => {
    if (labelInValue) {
      if (mode === 'multiple') {
        onChange?.(
          value.map(
            valueItem =>
              dataSource.find(item => item[fieldNamesValue] === valueItem.value) || {
                [fieldNamesValue]: valueItem.value,
                [fieldNamesLabel]: valueItem.label,
              },
          ),
        )
      } else {
        onChange?.(
          value
            ? dataSource.find(item => item[fieldNamesValue] === value[fieldNamesValue]) || {
                [fieldNamesValue]: value.value,
                [fieldNamesLabel]: value.label,
              }
            : undefined,
        )
      }
    } else {
      onChange?.(value, option)
    }
  }

  /**@type {import("antd/es/select").SelectProps} */
  const selectProps = {
    loading,
    allowClear: true,
    showSearch: true,
    virtual: false,
    popupMatchSelectWidth: false,
    placeholder: '请选择',
    options: dataSource,
    optionFilterProp: fieldNamesLabel,
    optionLabelProp: fieldNamesLabel,
    fieldNames: { value: fieldNamesValue, label: fieldNamesLabel, ...fieldNamesOther },
    labelInValue,
    mode,
    maxTagCount: mode === 'multiple' ? 'responsive' : undefined,
    filterOption: (inputValue, option) =>
      option[fieldNamesLabel].toLowerCase().includes(inputValue.toLowerCase()) ||
      pinyinUtil.getPinyin(option[fieldNamesLabel]).toLowerCase().includes(inputValue.toLowerCase()),
    value: valueForAntd,
    onSearch: triggerSearch,
    onChange: triggerChange,
    onClick,
    onBlur,
    ...otherProps,
  }

  /**
   * 以table展示下拉选项
   * @returns
   */
  const popupRender = () => {
    const parentWidth = document
      .getElementById(popupRenderId?.current)
      ?.parentElement?.parentElement?.style?.minWidth?.replace('px', '')
    const maxWidth = columns.reduce((pre, cur) => add(pre, cur.width || 120), 0)
    return (
      <div
        id={popupRenderId.current}
        style={{ maxWidth: parentWidth > maxWidth ? parentWidth - 8 : maxWidth }}
        onMouseDown={e => {
          e.preventDefault()
          e.stopPropagation()
        }}
      >
        <Table
          rowKey={fieldNamesValue}
          loading={loading}
          showConfig={false}
          pagination={false}
          columns={columns}
          scroll={{ y: 300 }}
          dataSource={dataSource}
          onRow={record => ({
            className: (
              labelInValue
                ? mode === 'multiple'
                  ? value?.some(item => record[fieldNamesValue] === item[fieldNamesValue])
                  : record[fieldNamesValue] === value?.[fieldNamesValue]
                : mode === 'multiple'
                  ? value?.some(item => record[fieldNamesValue] === item)
                  : record[fieldNamesValue] === value
            )
              ? 'bg-sel'
              : '',
            onClick: e => {
              e.preventDefault()
              e.stopPropagation()
              const newValue = labelInValue ? record : record[fieldNamesValue]
              if (mode === 'multiple') {
                const newValueList = [...(value || [])]
                const findedIndex = newValueList.findIndex(item =>
                  labelInValue ? item[fieldNamesValue] === newValue[fieldNamesValue] : item === newValue,
                )
                if (findedIndex > -1) {
                  newValueList.splice(findedIndex, 1)
                } else {
                  newValueList.push(newValue)
                }
                props.onChange?.(newValueList)
                return
              }
              setOpen(false)
              props.onChange?.(newValue)
            },
          })}
        />
      </div>
    )
  }

  if (columns.length) {
    selectProps.popupRender = popupRender
    selectProps.open = open
    selectProps.onClick = (...args) => {
      setOpen(!open)
      onClick?.(...args)
    }
    selectProps.onBlur = (...args) => {
      setOpen(false)
      onBlur?.(...args)
    }
  }

  return <Select ref={ref} {...selectProps} />
}
export default forwardRef(AekSelect)
